using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage; 
using Demo;
using System;
using System.Threading;

namespace DeferredWaterDemo
{
    public partial class Core : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;

        private static Core inst;
        public Camera camera;
       static float time = 0f;

       public static bool IsNull(object ptr)
       {
           return (ptr == null);
       }

       Mesh GeometryScene;
       Mesh GeometrySky;
       Mesh GeometryGrass;
       Texture SceneLightMap;
       Texture WaterNormalMap;
       Texture WaterHeightMap;
       Texture WaterFoamMap;
       Texture WaterCausticsMap;
       Texture SandNormalMap;
       List<Matrix> GrassInstances;
       Random Generator = new Random();
                
        public Core()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth =  800;
            graphics.PreferredBackBufferHeight =  600;
            Content.RootDirectory = "Content";
        }

        public static float Time
        {
            get { return time; }
        }
        protected override void Initialize()
        {
            inst = this;
            Window.Title = "rt-defwater";
            base.Initialize();
        }

        private void LoadSceneLight(EnginePointlight light)
        {
        }

        private void LoadSceneObject(string Name, Matrix transform, List<ObjectProperty> properties)
        {
            if (Name == "sky")
            {
                if (IsNull(GeometrySky))
                    GeometrySky = new Mesh();
                if (!GeometrySky.Create(".\\Content\\Models\\sky.mdl", null, null, null, null))
                    throw new Exception("Failed to load sky");
            }
            else if (Name == "grass")
            {
                if (IsNull(GeometryGrass)) 
                {
                    GeometryGrass = new Mesh();
                    if(!GeometryGrass.Create(".\\Content\\Models\\grass.mdl", null, null, null, null))
                        throw new Exception("Failed to load grass");
                }
                if (IsNull(GrassInstances))
                {
                    GrassInstances = new List<Matrix>();
                }

                float scale = 1f + (float)Generator.NextDouble();
                
                GrassInstances.Add(Matrix.CreateScale(scale) * transform);
            }
        }

        private void LoadSceneProperties(List<ObjectProperty> properties)
        {
            foreach(ObjectProperty property in properties)
            {
                if (property.Name == "lightmap") 
                    EngineResource.LoadTexture(".\\Content\\Textures\\" + property.Value, out SceneLightMap); 
            }
        }

        protected override void LoadContent()
        {
            Render.Initialize(this);

            camera = new Camera(new Vector3(0, 10, -30), 0, 0, 1);
                        
            Components.Add(camera);

            //LoadScene("scene");
            LoadScene("container");
            
           
            EngineResource.LoadTexture(".\\Content\\Textures\\water_bump.png", out WaterNormalMap);
            EngineResource.LoadTexture(".\\Content\\Textures\\water_height.png", out WaterHeightMap);
            EngineResource.LoadTexture(".\\Content\\Textures\\sand_n.png", out SandNormalMap);
            EngineResource.LoadTexture(".\\Content\\Textures\\water_foam.png", out WaterFoamMap);
            EngineResource.LoadTexture(".\\Content\\Textures\\water_caustics.png", out WaterCausticsMap);
            
            

        }

        protected override void UnloadContent()
        {
            if (!IsNull(GeometryScene))
            {
                GeometryScene.Release();
                GeometryScene = null;
            }

            if (!IsNull(GeometrySky))
            {
                GeometrySky.Release();
                GeometrySky = null;
            }

            Render.Release();
        }

        private void LoadScene(string filename)
        {
            if (IsNull(filename))
                return;

            if (IsNull(GeometryScene))
                GeometryScene = new Mesh();
            else
                GeometryScene.Release();
            
            if (!IsNull(GrassInstances))
                GrassInstances.Clear();

            if (!GeometryScene.Create(".\\Content\\Models\\" + filename + ".mdl", LoadSceneProperties, LoadSceneLight, LoadSceneObject, null))
                throw new Exception("Failed to load scene");
        }

        protected override void Update(GameTime gameTime)
        {
            UpdateInput();

            time += gameTime.ElapsedGameTime.Milliseconds / 1000f;

            int reloadMask = 0;
            if (IsKeyDown(Keys.G))
                reloadMask = Mesh.GEOMETRY;
            if (IsKeyDown(Keys.H))
                reloadMask = Mesh.SHADER;
            if (IsKeyDown(Keys.T))
                reloadMask = Mesh.TEXTURE;
            if (IsKeyDown(Keys.R))
                reloadMask = Mesh.GEOMETRY | Mesh.SHADER | Mesh.TEXTURE;
            if(reloadMask != 0)
            {
                if ((reloadMask & Mesh.TEXTURE) != 0)
                {
                    EngineResource.LoadTexture(".\\Content\\Textures\\water_bump.png", out WaterNormalMap);
                    EngineResource.LoadTexture(".\\Content\\Textures\\water_height.png", out WaterHeightMap);
                    EngineResource.LoadTexture(".\\Content\\Textures\\staticlighting.png", out SceneLightMap);
                    EngineResource.LoadTexture(".\\Content\\Textures\\sand_n.png", out SandNormalMap);
                    EngineResource.LoadTexture(".\\Content\\Textures\\water_foam.png", out WaterFoamMap);
                    EngineResource.LoadTexture(".\\Content\\Textures\\water_caustics.png", out WaterCausticsMap);
                }
                else
                {
                    GeometryScene.Reload(reloadMask);
                    if (!IsNull(GeometryGrass)) GeometryGrass.Reload(reloadMask);
                    if (!IsNull(GeometrySky)) GeometrySky.Reload(reloadMask);
                }
            }
            if (IsKeyDown(Keys.C))
                camera.Position = new Vector3(0, 10, -30);

            if (IsKeyDown(Keys.Z))
                LoadScene("scene");
            if (IsKeyDown(Keys.X))
                LoadScene("container");
            
            base.Update(gameTime);
        }

        public static Core Demo
        {
            get { return inst; }
        }
        public static Camera Camera
        {
            get { return inst.camera; }
        }

        private void DrawScene(Effect shader)
        {
            if (!Core.IsNull(GeometrySky))
            {
                GraphicsDevice.RenderState.DepthBufferEnable = false;
                GeometrySky.Draw(Matrix.Identity, shader);
                GraphicsDevice.RenderState.DepthBufferEnable = true;
            }
            GraphicsDevice.Textures[0] = SceneLightMap;
            GeometryScene.Draw(Matrix.Identity, shader);

            if (IsNull(shader) && !IsNull(GrassInstances))
            {
                GraphicsDevice.RenderState.DepthBufferWriteEnable = false;
                GraphicsDevice.RenderState.AlphaBlendEnable = true;
                GraphicsDevice.RenderState.CullMode = CullMode.None;
                foreach (Matrix t in GrassInstances)
                {
                    GeometryGrass.Draw(t, null);
                }
                GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
                GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
                GraphicsDevice.RenderState.AlphaBlendEnable = false;
            }
        }

        Texture check;
        private void SetupDeferred()
        {
            if (check == null)
                EngineResource.LoadTexture(".\\Content\\Textures\\check.png", out check);

            GraphicsDevice.Textures[4] = IsNull(GeometrySky) ? null : GeometrySky.PartTexture(0);
            GraphicsDevice.Textures[5] = WaterNormalMap;
            GraphicsDevice.Textures[6] = WaterHeightMap;
            GraphicsDevice.Textures[7] = SandNormalMap;
            GraphicsDevice.Textures[8] = WaterFoamMap;
            GraphicsDevice.Textures[9] = WaterCausticsMap;
        }

        protected override void Draw(GameTime gameTime)
        {
            Render.Draw(DrawScene, SetupDeferred);
            Thread.Sleep(10);
        }
    }
}
